﻿using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using adminTemplateApi.Data;
using adminTemplateApi.Model.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace adminTemplateApi.Controllers
{
    [Route("api/[controller]/[action]")]    
    [ApiController]
    // [CommonResultFilter] //add in global config
    public abstract class BaseController<T,TKey> : ControllerBase where T : class
    {
        protected readonly DBContext db;

        public BaseController(DBContext context)
        {
            db = context;
        }

        protected abstract TKey GetIdFromObj(T obj);

        [HttpGet]
        public virtual async Task<ActionResult<IEnumerable<T>>> GetAll()
        {
            return await db.Set<T>().ToListAsync();
            
        }

        [HttpGet("{id}")]
        public virtual async Task<ActionResult<T>> GetOne(TKey id)
        {
            return await db.Set<T>().FindAsync(id);
        }

        [HttpPost]
        public virtual async Task<IActionResult> Add(T obj) { 
            db.Set<T>().Add(obj);
            await db.SaveChangesAsync();
            return CreatedAtAction("GetOne", new { id = GetIdFromObj(obj) }, obj);
        }

        [HttpPost("{id}")]
        public virtual async Task<IActionResult> Update(TKey id, T obj) {
            if (!GetIdFromObj(obj).Equals(id))
            {
                return BadRequest();
            }

            db.Entry(obj).State = EntityState.Modified;
            try {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException) {
                var dbobj = await db.Set<T>().FindAsync(GetIdFromObj(obj));
                if (dbobj == null)
                {
                    return NotFound();
                }
                throw;
            }
            return NoContent();
        }

        [HttpGet("{id}")]
        public virtual async Task<IActionResult> Delete(TKey id) {
            var obj = await db.Set<T>().FindAsync(id);
            if (obj == null) {
                return NotFound();
            }
            db.Set<T>().Remove(obj);
            await db.SaveChangesAsync();

            return NoContent();
        }

        [HttpPost]
        public virtual async Task<ActionResult<Page<T>>> Search(SearchVm<T> search)
        {
            if (search.CurrentPage < 1) search.CurrentPage = 1;
            if (search.PageSize < 1) search.PageSize = 10;
            var skip = (search.CurrentPage - 1) * search.PageSize;

            var set = db.Set<T>().AsQueryable();
            set = set.Where(getWhere(search));
            if (string.IsNullOrEmpty(search.OrderBy))
            {
                search.OrderBy = "id";
            }

            if (search.desc)
            {
                search.OrderBy += " desc";
            }

            set = set.OrderBy(search.OrderBy);
            

            var r = new Page<T>();
            r.CurrentPage = search.CurrentPage;
            r.PageSize = search.PageSize;

            r.TotalCount = await set.CountAsync();
            var pagecnt = r.TotalCount / r.PageSize;
            if (r.TotalCount % r.PageSize > 0) pagecnt++;
            r.TotalPages = pagecnt;
            r.Items = await set.Skip(skip).Take(search.PageSize).ToListAsync();
            return r;
        }

        protected virtual Expression<Func<T, bool>> getWhere(SearchVm<T> search)
        {
            return x => true;
        }


    }
}
